from influxdb_client import InfluxDBClient
import pytz
import json
import time
from datetime import datetime, timedelta, timezone
# 在文件顶部添加以下导入
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from app import Mem, Net, Disk, Kernel
from app.models.diskio import Diskio  # 根据实际模型路径调整
from app.models.cpu import Cpu
from app.models.mem import Mem
from app.models.net import Net
from app.models.disk import Disk
from app.models.kernel import Kernel
# 添加MySQL配置（根据实际配置修改）
# 正确声明SQLAlchemy实例

MYSQL_URI = 'mysql+pymysql://root:wzw123456@localhost/kylin_2025'
engine = create_engine(MYSQL_URI)
Session = sessionmaker(bind=engine)
# 配置参数
url = "http://192.168.31.169:8086"
token = "xnm_YMl2iEkkoFmewgKqJ5K4_jC75wYxBRPYMQZsbtaMu4H0NpeFqel0cR0OLB6cb96aSpqX88Bn6B5MsUlkww=="
org = "0b33806f52c26ec7"
bucket = "kylin"

# 缓存上一次的累计值，key包含设备名，确保设备区分
last_diskio_iotime = {}

with open('exception_rules.json', 'r') as f:
    rules = json.load(f)

RED_EXCLAMATION = "!" * 13


def is_abnormal(value, threshold, condition):
    if condition == "gt":
        return value > threshold
    elif condition == "lt":
        return value < threshold
    else:
        return False


def is_abnormal_multi(value, rules_list):
    for rule in rules_list:
        threshold = rule.get("threshold")
        condition = rule.get("condition")
        if is_abnormal(value, threshold, condition):
            return True
    return False


def query_new_data(client, bucket, org, start_time):
    query = f'''
    from(bucket:"{bucket}")
      |> range(start: {start_time})
      |> sort(columns: ["_time"])
    '''
    query_api = client.query_api()
    result = query_api.query(query=query)
    records = []
    for table in result:
        for record in table.records:
            records.append(record)
    return records


def extract_tags(record):
    # 过滤InfluxDB内部字段，保留业务标签
    tags = {k: v for k, v in record.values.items() if k not in ['_time', '_measurement', '_field', '_value']}
    return tags


def main():
    global last_diskio_iotime, rules
    db_session = Session()  # 创建数据库会话
    # 使用上下文管理器创建 InfluxDB 客户端连接
    with InfluxDBClient(url=url, token=token, org=org) as client:
        # 使用上下文管理器创建 InfluxDB 客户端连接
        last_time = (datetime.now(timezone.utc) - timedelta(minutes=1)).isoformat()
        while True:
            # 读取新数据
            records = query_new_data(client, bucket, org, last_time)
            if records:
                for record in records:
                    utc_time = record.get_time() # 原始UTC时间戳（来自Influ
                    local_time = utc_time.astimezone(pytz.timezone("Asia/Shanghai"))# 转换后的北京时间（用于本地化展示）
                    measurement = record.get_measurement()# 指标大类（对应kylin_2025数据库表名，如cpu/memory/disk）
                    field = record.get_field()# 具体指标字段（对应数据库表的metric_name字段）
                    value = record.get_value()# 指标数值（对应数据库表的value字段）
                    # 过滤InfluxDB内部字段，保留业务标签
                    tags = extract_tags(record)

                    # 补充常见标签字段，确保完整性
                    device = tags.get("device") or tags.get("name") or "unknown"# 设备标识（对应disk表的device字段，如/dev/sda1）
                    interface = tags.get("interface", "unknown")# 网络接口名（对应network表的interface字段，如eth0）
                    serial = tags.get("serial", "unknown")# 硬件序列号（对应disk表的serial字段，唯一标识）
                    irq = tags.get("irq", None)# 中断号（对应kernel表的irq字段，用于诊断硬件问题）
                    fstype = tags.get("fstype", None)# 文件系统类型（对应disk表的fstype字段，如ext4）
                    mode = tags.get("mode", None)# 挂载模式（对应disk表的mode字段，如rw/ro）
                    path = tags.get("path", None)# 挂载路径（对应disk表的path字段，如/data）
                    label = tags.get("label", None)# 自定义标签（对应disk表的label字段，用于业务分类）

                    prefix = ""

                    # 针对 diskio 指标的 io_time 字段进行特殊处理（累计值需要计算增量）
                    if measurement == "diskio" and field == "io_time":
                        # 生成设备唯一标识：指标类型+字段名+设备名（如 ('diskio', 'io_time', 'sda1')）
                        key = (measurement, field, device)
                        # 从缓存字典获取该设备上次记录的io_time值
                        prev_value = last_diskio_iotime.get(key)

                        if prev_value is None:  # 首次获取该设备数据
                            # 初始化缓存（记录当前值供下次计算增量）
                            last_diskio_iotime[key] = value
                            # 打印初始化信息（无差值可计算）
                            print(
                                f"Time: {local_time}, Measurement: {measurement}, Field: {field}, Device: {device}, Initial io_time value: {value} (no diff)")
                        else:
                            # 计算当前值与上次值的差值（处理计数器重置的情况）
                            diff = value - prev_value
                            if diff < 0:  # 处理计数器归零的情况（如设备重启）
                                diff = 0

                            # 获取该测量类型的异常规则配置（如diskio的io_time阈值）
                            measurement_rules = rules.get(measurement, {})
                            field_rule = measurement_rules.get(field)

                            if field_rule:  # 存在异常检测规则时
                                if isinstance(field_rule, list):  # 多条件规则（如多个阈值区间）
                                    if is_abnormal_multi(diff, field_rule):
                                        prefix = RED_EXCLAMATION  # 标记异常
                                else:  # 单条件规则（如单一阈值）
                                    if is_abnormal(diff, field_rule.get("threshold"), field_rule.get("condition")):
                                        prefix = RED_EXCLAMATION

                            # 打印带异常标记的实时增量信息
                            print(
                                f"{prefix}Time: {local_time}, Measurement: {measurement}, Field: {field}, Device: {device}, Real-time io_time increment: {diff}")
                            # 更新缓存为当前值，供下次计算使用
                            last_diskio_iotime[key] = value
                    else:  # 处理非diskio/io_time的其他指标
                        # 获取当前测量类型的异常规则配置（如cpu.usage的阈值）
                        measurement_rules = rules.get(measurement, {})
                        field_rule = measurement_rules.get(field)

                        if field_rule:  # 存在异常检测规则时
                            if isinstance(field_rule, list):  # 多条件规则
                                if is_abnormal_multi(value, field_rule):
                                    prefix = RED_EXCLAMATION
                            else:  # 单条件规则
                                if is_abnormal(value, field_rule.get("threshold"), field_rule.get("condition")):
                                    prefix = RED_EXCLAMATION

                        # 打印完整设备信息（包含所有标签字段）
                        print(
                            f"{prefix}Time: {local_time}, Measurement: {measurement}, Field: {field}, Value: {value}, "
                            f"Device: {device}, Interface: {interface}, Serial: {serial}, IRQ: {irq}, "
                            f"Fstype: {fstype}, Mode: {mode}, Path: {path}, Label: {label}")

                    # 分隔线（每批数据处理完成后打印）
                    print("-" * 50)
                    # 新增异常数据存储逻辑
                    if prefix:  # 当检测到异常时
                        try:
                            # 根据measurement类型选择模型
                            if measurement == "diskio":
                                record = Diskio(
                                    serial=serial,
                                    metric_name=field,
                                    value=diff if field == "io_time" else value,
                                    timestamp_value=local_time.replace(tzinfo=None)
                                )
                            elif measurement == "cpu":
                                record = Cpu(
                                    metric_name=field,
                                    value=value,
                                    timestamp_value=local_time.replace(tzinfo=None)
                                )
                            elif measurement == "mem":
                                record = Mem(
                                    metric_name=field,
                                    value=value,
                                    timestamp_value=local_time.replace(tzinfo=None)
                                )
                            elif measurement == "net":
                                record = Net(
                                    interface=interface,
                                    metric_name=field,
                                    value=value,
                                    timestamp_value=local_time.replace(tzinfo=None)
                                )
                            elif measurement == "disk":
                                record = Disk(
                                    device=device,
                                    path=path,
                                    metric_name=field,
                                    value=value,
                                    timestamp_value=local_time.replace(tzinfo=None)
                                )
                            elif measurement == "kernel":
                                record = Kernel(
                                    irq=irq,
                                    metric_name=field,
                                    value=value,
                                    timestamp_value=local_time.replace(tzinfo=None)
                                )

                            db_session.add(record)
                            db_session.commit()
                            print(f"异常数据已存储到MySQL: {measurement}.{field}")
                        except Exception as e:
                            print(f"数据库写入失败: {str(e)}")
                            db_session.rollback()
                        db_session.add(record)
                        db_session.commit()
            time.sleep(10)


if __name__ == "__main__":
    main()
